package com.mazaiting.auth.controller;

import com.alibaba.fastjson.JSON;
import com.mazaiting.auth.constant.SecurityConst;
import com.mazaiting.auth.domain.ClientInfo;
import com.mazaiting.auth.domain.LoginVO;
import com.mazaiting.auth.domain.WechatLoginVO;
import com.mazaiting.auth.utils.RequestUtil;
import com.mazaiting.common.core.ex.exceptions.EmptyObjectException;
import com.mazaiting.common.core.utils.MapUtil;
import com.mazaiting.web.utils.JwtUtil;
import com.mazaiting.redis.service.IRedisStringService;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;

import java.security.KeyPair;
import java.security.Principal;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 认证控制器
 */
@Slf4j
@RequestMapping("auth/v1/oauth")
@RestController
@RequiredArgsConstructor
public class OauthController {
    private final KeyPair keyPair;
    private final TokenEndpoint tokenEndpoint;
    private final IRedisStringService redisStringService;

    /**
     * 登录接口
     *
     * @param loginVO 登录实体
     * @return 认证令牌
     */
    @PostMapping("login")
    public OAuth2AccessToken login(LoginVO loginVO) throws HttpRequestMethodNotSupportedException, EmptyObjectException {
        return login(MapUtil.beanToMap(loginVO));
    }

    /**
     * 登录接口
     *
     * @param loginVO 登录实体
     */
    @PostMapping("wechatLogin")
    public OAuth2AccessToken wechatMiniAppLogin(WechatLoginVO loginVO) throws HttpRequestMethodNotSupportedException, EmptyObjectException {
        return login(MapUtil.beanToMap(loginVO));
    }

    /**
     * 刷新 token 接口
     */
    @PostMapping("refresh")
    public OAuth2AccessToken refresh() throws HttpRequestMethodNotSupportedException, EmptyObjectException {
        ClientInfo clientInfo = RequestUtil.getOAuth2ClientId();
        log.info("刷新-OAuth认证授权 客户端id:{}, 客户端secret: {}", clientInfo.getClientId(), clientInfo.getClientSecret());
        Map<String, String> parameters = new HashMap<>();
//        parameters.put("username", authLoginDto.getUsername());
//        parameters.put("password", authLoginDto.getPassword());
        parameters.put("grant_type", RequestUtil.getGrantType());
        parameters.put("refresh_token", RequestUtil.getRefreshToken());
        //客户端信息
        User u = new User(clientInfo.getClientId(), clientInfo.getClientSecret(), new ArrayList<>());
        //生成已经认证的client
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(u, null, new ArrayList<>());
        ResponseEntity<OAuth2AccessToken> responseEntity = tokenEndpoint.postAccessToken(token, parameters);

        return responseEntity.getBody();
    }

    /**
     * 退出登录
     */
    @DeleteMapping("/logout")
    public void logout() throws EmptyObjectException {
        JwtUtil.getPayloadInfo().ifPresent(payload -> {
            // JWT唯一标识
            String jti = payload.getJti();
            // JWT过期时间戳(单位：秒)
            Long expireTime = payload.getExpire();
            if (expireTime != null) {
                // 当前时间（单位：秒）
                long currentTime = System.currentTimeMillis() / 1000;
                // token未过期，添加至缓存作为黑名单限制访问，缓存时间为token过期剩余时间
                if (expireTime > currentTime) {
                    redisStringService.setEx(SecurityConst.TOKEN_BLACKLIST_PREFIX + jti, "", (expireTime - currentTime), TimeUnit.SECONDS);
                }
            } else {
                // token 永不过期则永久加入黑名单
                redisStringService.set(SecurityConst.TOKEN_BLACKLIST_PREFIX + jti, "");
            }
        });
    }

    /**
     * 获取公钥
     * 此接口需要原样返回, 响应不能包装
     */
    @GetMapping("publicKey")
    public String getPublicKey() {
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAKey key = new RSAKey.Builder(publicKey).build();
        Map<String, Object> map = new JWKSet(key).toJSONObject();
        return JSON.toJSONString(map);
    }

    /**
     * 登录
     * @param parameters 参数
     * @return 认证令牌
     */
    public OAuth2AccessToken login(Map<String, String> parameters) throws EmptyObjectException, HttpRequestMethodNotSupportedException {
        /*
         * 获取登录认证的客户端ID
         *
         * 兼容两种方式获取Oauth2客户端信息（client_id、client_secret）
         * 方式一：loginVO 中取
         * 方式二: 从请求头中取: 放在请求头（Request Headers）中的Authorization字段，且经过加密，例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret
         */
        ClientInfo clientInfo = RequestUtil.getOAuth2ClientId();
        String clientId = clientInfo.getClientId();
        log.info("登录-OAuth认证授权 客户端id:{}, 客户端secret: {}", clientInfo.getClientId(), clientInfo.getClientSecret());
//        Map<String, String> parameters = new HashMap<>();
//        parameters.put(SecurityConst.KEY_USER_NAME, loginVO.getUsername());
//        parameters.put(SecurityConst.KEY_PASSWORD, loginVO.getPassword());
//        parameters.put(SecurityConst.KEY_UUID, loginVO.getUuid());
//        parameters.put(SecurityConst.KEY_CODE, loginVO.getCode());
        // 获取授权类型
        String grantType = RequestUtil.getGrantType();
        parameters.put(SecurityConst.KEY_GRANT_TYPE, grantType);

        //客户端信息
        User u = new User(clientId, clientInfo.getClientSecret(), new ArrayList<>());
        //生成已经认证的client
        Principal principal = new UsernamePasswordAuthenticationToken(u, null, new ArrayList<>());
        ResponseEntity<OAuth2AccessToken> responseEntity = tokenEndpoint.postAccessToken(principal, parameters);

        return responseEntity.getBody();
    }
}
